//
//  LBRouterHandler.m
//  LBRouter
//
//  Created by WuShiHai on 19/5/24.
//  Copyright (c) 2019 Lebbay. All rights reserved.
//

#import "LBRouterHandler.h"
#import <LBFoundation/LBFoundation.h>
#import <LBFoundation/LBService.h>
#import <LBUIKit/LBUIKit.h>
#import "LBRouter.h"
#import "LBRouterHandler+Native.h"

NSString *const LBRouterParameterNeedLogin = @"token";
NSString *const LBRouterReactNativeModulePrefixPath = @"react";

static NSMutableDictionary<NSString *, Class> *LBControllerRouterConfig;
NSMutableDictionary<NSString *, Class> *LBGetNativeRouterConfig(void) {
    return LBControllerRouterConfig;
}

void LBRegisterControllerRouter(NSString *,Class);
void LBRegisterControllerRouter(NSString *router, Class moduleClass) {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        LBControllerRouterConfig = [NSMutableDictionary new];
    });
    // Register router
    if ( router ) {
        [LBControllerRouterConfig setObject:moduleClass forKey:router];
    }
}

@interface LBRouterHandler ()

@end

@implementation LBRouterHandler

+ (NSDictionary<NSString *, Class> *)controllerRouter {
    return LBControllerRouterConfig;
}

+ (NSString *)routeWithNativeClass:(Class)clazz {
    NSDictionary<NSString *, Class> *controllerRouter = [LBRouterHandler controllerRouter];
    if ([controllerRouter.allValues containsObject:clazz]) {
        __block NSString *router = nil;
        [controllerRouter enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, Class  _Nonnull obj, BOOL * _Nonnull stop) {
            if ([NSStringFromClass(clazz) isEqualToString:NSStringFromClass(obj)]) {
                router = key;
                *stop = YES;
            }
        }];
        return router;
    }
    return nil;
}

#pragma mark - Handle

+ (void)openToNextController:(UIViewController *)vc
                      isPush:(BOOL)isPush
                 routerParam:(NSDictionary *)routerParameters {
    UIViewController *from = [UIApplication displayViewController];
    NSUInteger openStyle = [[routerParameters[LBRouterExtractParameterUserInfo] objectForKey:@"openStyle"] integerValue];
    BOOL animated = openStyle == 0;
    if (isPush) {
        if (openStyle == 3) {
            id<UINavigationControllerDelegate> originDelegate = vc.navigationController.delegate;
            from.navigationController.delegate = vc;
            [from.navigationController pushViewController:vc animated:YES];            
        } else {
            [from.navigationController pushViewController:vc animated:animated];
        }
    } else {
        if ([[vc class] respondsToSelector:@selector(presentedNavigationController:)]) {
            vc = [UIViewController presentedNavigationController:vc];
        }
        vc.modalPresentationStyle = UIModalPresentationFullScreen;
        [from presentViewController:vc
                           animated:YES
                         completion:nil];
    }
}

+ (void)handleRouterParam:(NSDictionary *)routerParameters {
    NSString *url = [self URLString:routerParameters];
    NSString *matchPath = [LBRouter matchPathWithURLString:url];
    if (matchPath) {
        if ([matchPath hasPrefix:LBRouterReactNativeModulePrefixPath]) {
            NSString *component = [self reactNativeNoduleNameFromMatchPath:matchPath];
            [self routeWithReactComponent:component
                                matchPath:matchPath
                         routerParameters:routerParameters
                      nativeConfigurePath:matchPath];
        } else {
            NSString *configurePath = [self keyFromNativeConfigureWithMatchPath:matchPath];
            if (configurePath) {
                NSDictionary<NSString *, Class> *routerNativeConfig = LBGetNativeRouterConfig();
                Class clazz = [routerNativeConfig objectForKey:configurePath];
                [self routeWithNativeClass:clazz
                                 matchPath:matchPath
                          routerParameters:routerParameters
                       nativeConfigurePath:configurePath];
            } else if ([LBRouterHandler isNativeCallbackRouter:matchPath]) {
                [self callbackCompleteHandler:routerParameters
                                         type:LBRouterTypeNativeCallback
                                    matchPath:[LBRouterHandler nativeCallbackMethod:matchPath]];
            } else if ([LBRouterHandler isNativeServiceRouter:matchPath]) {
                [LBRouterHandler callNativeService:matchPath
                                          userInfo:[self userInfo:routerParameters]];
                [self callbackCompleteHandler:routerParameters
                                         type:LBRouterTypeNativeMethod
                                    matchPath:matchPath];
            } else {
                [self openWebViewController:url
                                  matchPath:matchPath
                           routerParameters:routerParameters];
            }
        }
    } else {
        [self openWebViewController:url
                          matchPath:matchPath
                   routerParameters:routerParameters];
    }
}

+ (void)openWebViewController:(NSString *)URLString
                    matchPath:(NSString *)matchPath
             routerParameters:(NSDictionary *)routerParameters {
    if (URLString.length > 0 && [URLString hasPrefix:@"http"]) {
        UIViewController *vc = [LBServiceManager webController:URLString
                                                      userInfo:[self userInfo:routerParameters]];
        [self routeWithViewController:vc
                                 type:LBRouterTypeWebView
                            matchPath:matchPath
                     routerParameters:routerParameters
                  nativeConfigurePath:nil
                               isPush:[self isPushFrom:routerParameters]
                            pushAfter:0];
    } else {
//        NSAssert1(NO, @"Router:无法使用的-%@",URLString);
    }
}

+ (void)routeWithNativeClass:(Class)clazz
                   matchPath:(NSString *)matchPath
            routerParameters:(NSDictionary *)routerParameters
         nativeConfigurePath:(NSString *)nativeConfigurePath {
    if ([clazz isSubclassOfClass:[UITabBarController class]]) {
        [self routeWithTabBarControllerClass:clazz
                                   matchPath:matchPath
                            routerParameters:routerParameters
                         nativeConfigurePath:nativeConfigurePath];
    } else if ([clazz isSubclassOfClass:[UIViewController class]]) {
        BOOL isPush = YES;
        if ([clazz respondsToSelector:@selector(isOpenWithPush)]) {
            isPush = [clazz isOpenWithPush];
        }
        [self routeWithViewController:[[clazz alloc] initWithURL:[self URLString:routerParameters]
                                                        userInfo:[self userInfo:routerParameters]]
                                 type:LBRouterTypeNativeController
                            matchPath:matchPath
                     routerParameters:routerParameters
                  nativeConfigurePath:nativeConfigurePath
                               isPush:isPush
                            pushAfter:0];
    } else {
//        NSAssert(NO, @"clazz 类型错误");
    }
}

+ (void)routeWithTabBarControllerClass:(Class)class
                             matchPath:(NSString *)matchPath
                      routerParameters:(NSDictionary *)routerParameters
                   nativeConfigurePath:(NSString *)nativeConfigurePath {
    
    UIViewController *viewController = [UIApplication displayViewController];
    UITabBarController *tabBarController = [self findTabBarController:viewController
                                                                class:class];
    BOOL isPush = NO;
    if (!tabBarController) {
        isPush = YES;
        tabBarController = [[class alloc] initWithURL:[self URLString:routerParameters]
                                             userInfo:[self userInfo:routerParameters]];
        if ([tabBarController isKindOfClass:[LBTabBarController class]]) {
            [tabBarController setViewControllers:[(LBTabBarController *)class viewControllers]];
        }
    }
    
    UIViewController *lastSelectedViewController = tabBarController.selectedViewController;
    void (^doBlock)(void) = ^{
        BOOL isSuccessSelectTabBar = [self isSuccessSelectTabBar:tabBarController
                                                routerParameters:routerParameters];
        if (isSuccessSelectTabBar) {
            if (isPush) {
                [self routeWithViewController:tabBarController
                                         type:LBRouterTypeNativeController
                                    matchPath:matchPath
                             routerParameters:routerParameters
                          nativeConfigurePath:nativeConfigurePath
                                       isPush:[self isPushFrom:routerParameters]
                                    pushAfter:0];
            } else {
                //要pop回去
                [self backToTabBarController:tabBarController];
                if ([lastSelectedViewController isKindOfClass:[UINavigationController class]]) {
                    [(UINavigationController *)lastSelectedViewController popToRootViewControllerAnimated:NO];
                }
            }
        }
    };
    
    [self routeWithAction:doBlock
                       vc:tabBarController
                     type:LBRouterTypeNativeController
                matchPath:matchPath
         routerParameters:routerParameters
      nativeConfigurePath:nativeConfigurePath];
}

+ (BOOL)isSuccessSelectTabBar:(UITabBarController *)tabBarController
             routerParameters:(NSDictionary *)routerParameters {
    NSDictionary *userInfo = [self userInfo:routerParameters];
    NSString *idKey = [userInfo objectForKey:@"id"];
    idKey = [idKey stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"/"]];
    if ([idKey isKindOfClass:[NSString class]]) {
        if ([idKey hasPrefix:LBRouterReactNativeModulePrefixPath]) {
            NSString *component = [self reactNativeNoduleNameFromMatchPath:idKey];
            __block NSInteger index = -1;
            [tabBarController.viewControllers enumerateObjectsUsingBlock:^(__kindof UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                if ([obj isKindOfClass:[UINavigationController class]]
                    && [LBServiceManager isReactController:[(UINavigationController *)obj viewControllers].firstObject
                                                 component:component]) {
                    index = idx;
                    *stop = YES;
                } else if ([LBServiceManager isReactController:obj
                                              component:component]) {
                    index = idx;
                    *stop = YES;
                }
            }];
            if (index >= 0) {
                [tabBarController setSelectedIndex:index];
                return YES;
            }
        } else {
            NSString *nativeConfigurePath = [self keyFromNativeConfigureWithMatchPath:idKey];
            if (nativeConfigurePath) {
                NSDictionary<NSString *, Class> *routerNativeConfig = LBGetNativeRouterConfig();
                Class clazz = [routerNativeConfig objectForKey:nativeConfigurePath];
                __block NSInteger index = -1;
                [tabBarController.viewControllers enumerateObjectsUsingBlock:^(__kindof UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                    if ([obj isKindOfClass:clazz] || ([obj isKindOfClass:[UINavigationController class]] && [[(UINavigationController *)obj viewControllers].firstObject isKindOfClass:clazz])) {
                        index = idx;
                        *stop = YES;
                    }
                }];
                if (index >= 0) {
                    [tabBarController setSelectedIndex:index];
                    return YES;
                }
            }
        }
    }
    return NO;
}

//只能打开一个tabbar，不会实例化多个
//需要测试
+ (UITabBarController *)findTabBarController:(UIViewController *)controller
                                       class:(Class)class {
    if ([controller isKindOfClass:class]) {
        return (UITabBarController *)controller;
    } else if ([controller isKindOfClass:[UINavigationController class]]) {
        __block UITabBarController *tabBarController = nil;
        [[(UINavigationController *)controller viewControllers] enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(__kindof UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if ([controller isKindOfClass:class]) {
                tabBarController = (UITabBarController *)controller;
                *stop = YES;
            }
        }];
        if (tabBarController) {
            return tabBarController;
        }
    }
    
    UIViewController *nextController = controller.navigationController?:controller.tabBarController;
    nextController = nextController?:[controller presentingViewController];
    if (nextController) {
        return [self findTabBarController:nextController
                                    class:class];
    }
    
    return nil;
}

+ (void)backToTabBarController:(UITabBarController *)tabBarController {
    
    [tabBarController.navigationController popToViewController:tabBarController animated:NO];
    
    if ([tabBarController.selectedViewController isKindOfClass:[UINavigationController class]]) {
        //如果tabbar处于其navigation的栈首，则使用动画跳转
        if (tabBarController.navigationController == nil || tabBarController.navigationController.viewControllers.firstObject == tabBarController) {
            [tabBarController.selectedViewController popToRootViewControllerAnimated:YES];
        }else{
            [tabBarController.selectedViewController popToRootViewControllerAnimated:NO];
        }
    }
    
    [tabBarController dismissViewControllerAnimated:YES completion:nil];
}

+ (void)routeWithReactComponent:(NSString *)component
                      matchPath:(NSString *)matchPath
               routerParameters:(NSDictionary *)routerParameters
            nativeConfigurePath:(NSString *)nativeConfigurePath {
    if ([component isKindOfClass:[NSString class]]) {
        UIViewController *vc = [LBServiceManager reactController:[self URLString:routerParameters]
                                                       component:component
                                                        userInfo:[self userInfo:routerParameters]];
        dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC);
        dispatch_after(time, dispatch_get_main_queue(), ^{
            [self routeWithViewController:vc
                                     type:LBRouterTypeReactNativeController
                                matchPath:matchPath
                         routerParameters:routerParameters
                      nativeConfigurePath:nativeConfigurePath
                                   isPush:[self isPushFrom:routerParameters]
                                pushAfter:0.1];
        });
    } else {
//        NSAssert(NO, @"module 类型错误");
    }
}

+ (void)routeWithViewController:(UIViewController *)vc
                           type:(LBRouterType)type
                      matchPath:(NSString *)matchPath
               routerParameters:(NSDictionary *)routerParameters
            nativeConfigurePath:(NSString *)nativeConfigurePath
                         isPush:(BOOL)isPush
                      pushAfter:(NSTimeInterval)pushAfter {
    
    BOOL isOpen = YES;
    //过滤掉登录Controller
    if (vc && [vc isKindOfClass:[LBServiceManager loginControllerClass]]) {
        isOpen = NO;
    }
    
    void (^doBlock)(void) = ^{
        if (vc && isOpen){
            if (pushAfter > 0) {
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(pushAfter * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    [self openToNextController:vc
                                        isPush:isPush
                                   routerParam:routerParameters];
                });
            }else{
                [self openToNextController:vc
                                    isPush:isPush
                               routerParam:routerParameters];
            }
        }
    };
    
    [self routeWithAction:doBlock
                       vc:vc
                     type:type
                matchPath:matchPath
         routerParameters:routerParameters
      nativeConfigurePath:nativeConfigurePath];
}

+ (void)routeWithAction:(void(^)(void))action
                     vc:(UIViewController *)vc
                   type:(LBRouterType)type
              matchPath:(NSString *)matchPath
       routerParameters:(NSDictionary *)routerParameters
    nativeConfigurePath:(NSString *)nativeConfigurePath {
    BOOL isNeedLogin = [self isNativeConfigureNeedLogin:nativeConfigurePath] || [self isRouterNeedLogin:routerParameters];
    if ([vc isKindOfClass:[LBServiceManager loginControllerClass]]) {
        isNeedLogin = YES;
    }
    void (^doBlock)(void) = ^{
        if (action) {
            action();
        }
        [self callbackCompleteHandler:routerParameters
                                 type:type
                            matchPath:matchPath];
    };
    
    if (isNeedLogin && [LBServiceManager isLogin] == NO) {
        [LBServiceManager login:^{
            //延迟 避免拿到的当前Viewcontroller为登录页面
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                doBlock();
            });
        }];
    } else {
        doBlock();
    }
}

+ (NSString *)reactNativeNoduleNameFromMatchPath:(NSString *)matchPath {
    NSString *module = [matchPath stringByReplacingOccurrencesOfString:[LBRouterReactNativeModulePrefixPath stringByAppendingString:@"/"]
                                                            withString:@""];
    //替换失败，matchPath == @"react"
    if ([module hasPrefix:LBRouterReactNativeModulePrefixPath]) {
        return nil;
    } else {
        return module;
    }
}

+ (void)callbackCompleteHandler:(NSDictionary *)routerParameters
                           type:(LBRouterType)type
                      matchPath:(NSString *)matchPath {
    //如果有完成回调，调用。 返回值传nil
    LBRouterOpenURLCompleteHandler completion = routerParameters[LBRouterParameterCompletion];
    if (completion) {
        completion(type, [self URLString:routerParameters], matchPath, [self userInfo:routerParameters]);
    }
}

+ (NSString *)keyFromNativeConfigureWithMatchPath:(NSString *)matchPath {
    NSDictionary<NSString *, Class> *routerNativeConfig = LBGetNativeRouterConfig();
    return [self keyFromArrary:routerNativeConfig.allKeys matchPath:matchPath];
}

+ (NSString *)keyFromArrary:(NSArray<NSString *> *)keys matchPath:(NSString *)matchPath {
    NSString *predicate = [matchPath stringByAppendingString:@"?"];
    NSArray<NSString *> *matchConfigures = [keys filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
        return [evaluatedObject isEqualToString:matchPath]
        || [evaluatedObject hasPrefix:predicate]
        || [evaluatedObject isEqualToString:[NSString stringWithFormat:@"/%@",matchPath]]
        || [evaluatedObject hasPrefix:[NSString stringWithFormat:@"/%@",predicate]];
    }]];
    
    if (matchConfigures.count > 0) {
        return matchConfigures.firstObject;
    }
    return nil;
}

+ (BOOL)isNativeConfigureNeedLogin:(NSString *)router {
    return [router containsString:@"token=!"];
}

+ (BOOL)isRouterNeedLogin:(NSDictionary *)routerParameters {
    NSDictionary *userInfo = [self userInfo:routerParameters];
    NSString *needLogin = userInfo[LBRouterParameterNeedLogin];
    return [needLogin isKindOfClass:[NSString class]] && [needLogin isEqualToString:@"!"];
}
+ (NSDictionary *)userInfo:(NSDictionary *)routerParameters {
    NSMutableDictionary *userInfo = [@{} mutableCopy];
    [userInfo addEntriesFromDictionary:routerParameters[LBRouterExtractParameterUserInfo]];
    [userInfo addEntriesFromDictionary:routerParameters[LBRouterParameterUserInfo]];
    return [userInfo copy];
}

+ (NSString *)URLString:(NSDictionary *)routerParameters {
    return routerParameters[LBRouterParameterURL];
}
//url 源头来自本地
+ (BOOL)isFromAPPURL:(NSInteger)type {
    return type == 0;
}

//url 源头来自服务端
+ (BOOL)isFromServerURL:(NSInteger)type {
    return type == 1;
}

+ (BOOL)isPushFrom:(NSDictionary *)routerParameters {
    return [[routerParameters[LBRouterExtractParameterUserInfo] objectForKey:@"openStyle"] integerValue] != 1;
}

@end
